import { ReactNode, useCallback, useContext, useRef, useState } from 'react'
import { message } from 'antd'
import { AxiosError } from 'axios'
import { useCookieState, useKeyPress, useMount } from 'ahooks'
import { useNavigate } from 'react-router-dom'
import { Check, Loader, X } from 'lucide-react'
import { cn } from '@/utils/cn'
import { Spacer } from '@/components/ui/spacer'
import { Button } from '@/components/ui/button'
import { useCameraMedia } from '@/hooks/use-camera-media'
import { getToken, login } from '@/api'
import { removeBase64Prefix } from '@/utils/base64'
import { AuthContext } from './layout'

/**
 * UI 状态对应的 UI 信息
 */
interface FaceViewUIInfo {
  // 提示文本
  tipText: string
  // 提示文本的颜色
  tipTextColor: string
  // 人脸视频的边框颜色
  videoBorderColor: string
  // 覆盖在人脸视频上的视图
  videoOverlay: ReactNode
}

export default function FaceView() {
  const navigate = useNavigate()
  const { idCard } = useContext(AuthContext)

  const [, setAccessCode] = useCookieState('accessCode')
  const [, setNickname] = useCookieState('nickname')

  const videoRef = useRef<HTMLVideoElement | null>(null)
  const canvasRef = useRef<HTMLCanvasElement | null>(null)

  const { takePhoto, closeVideo } = useCameraMedia({
    videoRef,
    canvasRef,
  })

  const [loadState, setLoadState] = useState<{
    // 表示正在验证
    submitLoading: boolean
    // 若为空，表示常规状态，若不为空，表示成功/失败状态
    verificationResult?: { success: boolean }
  }>({
    submitLoading: false,
    verificationResult: undefined,
  })

  const uiStates: Record<
    'idle' | 'loading' | 'success' | 'failure',
    FaceViewUIInfo
  > = {
    idle: {
      tipText: '请验证您的人脸信息',
      tipTextColor: 'text-foreground',
      videoBorderColor: 'border-accent-foreground',
      videoOverlay: null,
    },
    loading: {
      tipText: '人脸信息验证中，请稍后...',
      tipTextColor: 'text-foreground',
      videoBorderColor: 'border-accent-foreground',
      videoOverlay: (
        <>
          <div className='flex justify-center items-center size-8 mb-2'>
            <Loader className='size-6 animate-spin text-white/80' />
          </div>
          <div className='leading-none text-white'>验证中...</div>
        </>
      ),
    },
    success: {
      tipText: '人脸信息验证成功，即将跳转...',
      tipTextColor: 'text-green-500',
      videoBorderColor: 'border-green-500',
      videoOverlay: (
        <>
          <div className='flex justify-center items-center size-8 mb-2 rounded-full bg-green-500 text-white'>
            <Check className='size-6' />
          </div>
          <div className='leading-none text-white'>验证成功</div>
        </>
      ),
    },
    failure: {
      tipText: '人脸信息验证失败，请重试',
      tipTextColor: 'text-destructive',
      videoBorderColor: 'border-destructive',
      videoOverlay: (
        <>
          <div className='flex justify-center items-center size-8 mb-2 rounded-full bg-red-500 text-white'>
            <X className='size-6' />
          </div>
          <div className='leading-none text-white'>验证未通过</div>
        </>
      ),
    },
  }

  /**
   * 当前的 UI 状态，根据 submitLoading 和 verificationResult 两个值确定
   *
   * 当 loading 为 true 优先显示 loading
   * 否则根据 verificationResult 的三种情况显示：
   * 1. success 为 true：验证成功
   * 2. success 为 false：验证失败
   * 3. undefined：默认状态
   */
  const currentUIState = (() => {
    if (loadState.submitLoading) {
      return uiStates.loading
    }
    if (!loadState.verificationResult) {
      return uiStates.idle
    }
    if (loadState.verificationResult.success) {
      return uiStates.success
    } else {
      return uiStates.failure
    }
  })()

  /**
   * @param result - 若不为空，success 表示验证成功，failed 表示验证失败；若为空，表示开始验证
   */
  const updateLoadState = useCallback((result?: 'success' | 'failed') => {
    if (result) {
      // 验证完成
      setLoadState({
        submitLoading: false,
        verificationResult: { success: result === 'success' },
      })
      videoRef.current?.play()
    } else {
      // 开始验证
      setLoadState({
        submitLoading: true,
        verificationResult: undefined,
      })
      videoRef.current?.pause()
    }
    // 如果结果为失败，显示失败2秒后重置为默认状态
    if (result === 'failed') {
      setTimeout(() => {
        setLoadState({
          submitLoading: false,
          verificationResult: undefined,
        })
      }, 2000)
    }
  }, [])

  const handleSubmit = async () => {
    // 确保 UI 状态为默认状态，可以开始验证
    if (loadState.submitLoading || loadState.verificationResult?.success) return
    // 开始验证，UI 状态更新为“验证中”
    updateLoadState()
    // 从视频流中获取照片 base64 字符串
    const imageData = takePhoto()

    // 确保照片和身份证号存在
    if (!imageData || !idCard) {
      message.error('发生错误，请刷新重试')
      updateLoadState('failed')
      return
    }
    try {
      const res = await login({
        idcard: idCard,
        imageBase64: removeBase64Prefix(imageData),
      })
      if (res.status !== 200) {
        message.error(res.statusText)
        updateLoadState('failed')
        return
      }
      if (res.data.errcode !== 0) {
        message.error(res.data.errmsg)
        updateLoadState('failed')
        return
      }
      // 获取账号下的个人身份，判断条件为 userUuid 不为空
      const identity = res.data.result.useridList.find(
        (item) => item.userUuid && item.userUuid.length > 0,
      )
      // 确保个人身份存在
      if (!identity) {
        message.error('获取账号信息失败')
        updateLoadState('failed')
        return
      }
      // 调用一次换取 token 的接口，完成登录。不需要使用任何返回的值
      await getToken({
        userUuid: identity.userUuid,
        access_code: res.data.result.accessCode,
        clientInfo: { clientType: 3, deviceFingerprint: '123' },
      })
      // UI 状态更新为“成功”
      updateLoadState('success')
      // 将 accessCode 和用户姓名保存下来
      setAccessCode(res.data.result.accessCode)
      setNickname(identity.name)
      // 1.5 秒后跳转到首页
      setTimeout(async () => {
        navigate('/', { replace: true })
        closeVideo()
      }, 1500)
    } catch (error) {
      console.log(error)
      message.error((error as AxiosError).message)
      updateLoadState('failed')
      return
    }
  }

  useKeyPress('Enter', handleSubmit)

  useMount(() => {
    if (!idCard) navigate('/login', { replace: true })
  })

  return (
    <>
      <div className='flex flex-col items-center w-full h-full'>
        <span
          className={cn('text-base leading-none', currentUIState.tipTextColor)}
        >
          {currentUIState.tipText}
        </span>
        <Spacer className='h-8' />
        <div className='relative mb-auto'>
          <video
            ref={videoRef}
            autoPlay
            playsInline
            className={cn(
              'size-60 rounded-full overflow-hidden border-[4px] bg-black object-cover -scale-x-100',
              currentUIState.videoBorderColor,
            )}
          />
          <div
            className={cn(
              'z-50 absolute inset-1 flex flex-col justify-center items-center rounded-full',
              currentUIState.videoOverlay && 'bg-black/50',
              loadState.submitLoading && 'backdrop-blur',
            )}
          >
            {currentUIState.videoOverlay}
          </div>
        </div>
        <div className='flex items-center w-full space-x-4'>
          <Button
            variant='outline'
            className='w-full'
            onClick={() => navigate(-1)}
          >
            返回登录
          </Button>
          <Button
            className='w-full'
            disabled={loadState.submitLoading}
            onClick={handleSubmit}
          >
            {loadState.submitLoading && (
              <Loader className='size-4 mr-2 animate-spin' />
            )}
            确认拍照
          </Button>
        </div>
      </div>
      <canvas ref={canvasRef} className='hidden' />
    </>
  )
}
